﻿public namespace System
{	
	public class Timer
	{
		private var num id = 0;
		private var num delay = 0;
		private var bool isInterval = false;
		
		public ctor( num delay )
		{
			this.delay = delay;
		}

		public ctor( num delay, bool isInterval )
		{
			this.delay = delay;
			this.isInterval = isInterval;
		}
		
		public function Start()
		{
			this.Stop();
			if ( this.isInterval )
			{
				this.id = StartIntervalTimer( this.OnTimeout, this.delay );
			}
			else
			{
				this.id = StartTimer( this.OnTimeout, this.delay );
			}
		}
		
		public function Stop()
		{
			if ( this.id != 0 )
			{
				if ( this.isInterval )
				{
					StopIntervalTimer( this.id );
				}
				else
				{
					StopTimer( this.id );
				}
				this.id = 0;
			}
		}
		
		public event Timer Timeout;
		
		private function OnTimeout()
		{
			this.Timeout(this);
		}
	}
	
	@JSImport("$System.StartTimer")
	public abstract function StartTimer( void->void func, num delay) -> num;
	@JSImport("$System.StopTimer")
	public abstract function StopTimer( num timerID );
	@JSImport("$System.StartIntervalTimer")
	public abstract function StartIntervalTimer( void->void func, num interval) -> num;
	@JSImport("$System.StopIntervalTimer")
	public abstract function StopIntervalTimer( num timerID );
	
	public class LinearAnimation
	{
		private var num id = 0;
		private var num step;
		private var num steps;
		private var num stepDuration;
		private var num from;
		private var num to;
		private var string unit = "px";
		
		public ctor( num from, num to, num duration )
		{
			this.steps = System.Math.Ceil(duration / 50);
			this.stepDuration = 50;
			this.from = from;
			this.to = to;
		}
		
		public function Start()
		{
			this.Stop();
			this.step = 0;
			this.id = StartIntervalTimer( this.OnAnimation, this.stepDuration );
		}
		
		public function Stop()
		{
			if ( this.id != 0 )
			{
				StopIntervalTimer( this.id );
			}
			this.id = 0;
		}
		
		public event string PositionString;
		
		private function OnAnimation()
		{
			this.step++;
			if ( this.step == this.steps )
			{
				this.PositionString(this.to.ToString() + this.unit);
				this.Stop();
			}
			else
			{
				var diff = (this.to - this.from) / this.steps;
				this.PositionString( (this.from + this.step * diff).ToString() + this.unit);
			}				
		}
	}	
}

public namespace System.Math
{
	@JSImport("Math.ceil")
	public abstract function Ceil( num value ) -> num;
	@JSImport("Math.floor")
	public abstract function Floor( num value ) -> num;
}

public namespace System.Console
{
	@JSImport("$System.Console.WriteLine")
	public abstract function WriteLine(string str);
	@JSImport("$System.Console.WriteLine")
	public abstract function WriteLine();
	@JSImport("$System.Console.Write")
	public abstract function Write(string str);
	@JSImport("$System.Console.Write")
	public abstract function Write(num i);	
	@JSImport("$System.Console.Write")
	public abstract function Write(bool b);		
	@JSImport("$System.Console.WriteXML")
	public abstract function Write(System.HTML.Node node);		
	
}

public namespace System.HTML
{
	@JSNativeClass
	public abstract sealed class Event
	{
	}

	@JSNativeClass
	public abstract sealed class Style
	{
		@HTMLProperty("color")
		public abstract property string color { get; set; }
		@HTMLProperty("backgroundColor")
		public abstract property string backgroundColor { get; set; }
		@HTMLProperty("left")
		public abstract property string left { get; set; }
		@HTMLProperty("top")	
		public abstract property string top { get; set; }
	}

	@JSNativeClass
	public abstract class Node
	{
		@JSImport("appendChild")
		public abstract function AppendChild(Node node);
		@HTMLProperty("style")
		public abstract property Style style { get; }
		@HTMLEvent("click")
		public event Event click;
	}
		
	@JSImport("document.createElement")
	public abstract function CreateElement(string tag) -> Node;
	@JSImport("document.createTextNode")
	public abstract function CreateTextNode(string text) -> Node;
		
	@HTMLClass
	public abstract sealed class div : Node
	{
	}
	
	@HTMLClass
	public abstract sealed class span : Node
	{
	}

	@HTMLClass
	public abstract sealed class input : Node
	{
		@HTMLProperty("type")
		public abstract property string type { get; set; }
		@HTMLProperty("value")
		public abstract property string value { get; set; }
	}	
}

public namespace NSpace
{
	using System.Console;
	using System.HTML;
		
	System.Console.Write("Hallo Neue Welt");
	System.Console.WriteLine();
	
	public interface Vehicle
	{
		public abstract property num MaxSpeed { get; }
	}
	
	public class Car : Vehicle
	{
		public var string Kennzeichen;
		public property num MaxSpeed { get; set; }
		
		public function GetString() -> string { return this.Kennzeichen; }
		
		public function Malfunction(string name)
		{
			var Car x = null;
			x.GetString();
		}
	}
	
	public function Square(num x) -> num
	{
		return x*x;
	}
	
	var auto = new Car();
	auto.Kennzeichen = "MH-TW 943";
	auto.MaxSpeed = 220;
	System.Console.Write(auto.MaxSpeed);
	System.Console.WriteLine();
	System.Console.WriteLine(auto.Kennzeichen);
	auto.MaxSpeed += 10;
	System.Console.Write(auto.MaxSpeed);
	System.Console.WriteLine();
	System.Console.WriteLine(auto.Kennzeichen);
	auto.MaxSpeed++;
	System.Console.Write(auto.MaxSpeed);
	System.Console.WriteLine();
	
	var Vehicle v = auto;
	var Car c = v as Car;		
	System.Console.Write(c.MaxSpeed);
	System.Console.WriteLine();
	System.Console.Write(v is Car);
	System.Console.WriteLine();
	
	var lst = ["Audi", "BMW", "VW"];
	foreach( var m in lst )
	{
		System.Console.WriteLine(m);
	}
	var dict = {"Name" : "Hugo", "Nachname" : "Boss"};	
	
	var node = CreateElement("h1");
	node.AppendChild( CreateTextNode("Das ist ne Ueberschrift") );
	Write(node);
	
	var func = Square;
	System.Console.Write(func(5));
	System.Console.WriteLine();
	
	var void -> string func2 = auto.GetString;
	System.Console.WriteLine(func2());
	
	var num -> num mul = a -> 2*a;
	System.Console.Write(mul(21));		
	System.Console.WriteLine();
	
	var div = function(num b) -> num { return b / 2; };
	Write(div(42));		
	WriteLine();
	
	var string str = "font-weight:bold; color:#88FF88";
	
	var xml = <div>Mein Kennzeichen ist <span style="font-weight:bold; color:#88FF88">{func2()}</span></div>;	
	var xml1 = <div>Wunderbare<span>Hallo Eingabe \+ Neues<input /><span /><span></span></span>Welt<span>doof</span></div>;
	var xml2 = <div>{func2()}</div>;
	var xml3 = <div>{func2()}<span>{func2()}</span>{func2()}</div>;
	var xml4 = <input type="submit" value={func2()} click={a -> WriteLine(auto.Kennzeichen)} />;
	
	Write(xml);
	Write(xml4);
	
	xml.style.color = "blue";
	
	xml4.click => a -> WriteLine("Es macht klick");
	
	var car = <Car Kennzeichen="FB-XX 92" MaxSpeed={190} />;
	
	var sum = function(num a) -> num
	{
		var s = a;
		while( true )
		{ 
			yield return s; 
			s += a;
		}
	}; 
	
	Write(sum(5));
	Write(" ");
	Write(sum(6));
	Write(" ");
	Write(sum(7));
	WriteLine("====================");

	var sum2 = function(num a) -> num
	{
		var s = a;
		do
		{ 
			yield return s; 
			s += a;
		} while( true );
	}; 
	
	Write(sum2(6));
	Write(" ");
	Write(sum2(7));
	Write(" ");
	Write(sum2(8));
	WriteLine("====================");

	var filt = function(num a) -> string
	{
		var s = a;
		do
		{ 
			if ( s > 20 )
			{
				yield return "greater 20! ";
				yield return "oioioi";
			}
			else if ( s > 10 )
			{
				yield return "greater 10! ";
			}
			else
			{
				yield return "smaller or equal 10! ";	
			}
			s += a;
		} while( true );
	}; 
	
	Write(filt(6));
	Write(" ");
	Write(filt(7));
	Write(" ");
	Write(filt(8));
	Write(" ");
	Write(filt(9));
	WriteLine("====================");
	
	var range = function(num f, num to ) -> (void -> num)
	{
		return function() -> num
		{
			for( var i = f; i <= to; ++i )
			{
				yield return i;
			}
		};
	};
	
	var count = range(4,10);
	Write(count());
	Write(" ");
	Write(count());
	Write(" ");
	Write(count());
	Write(" ");
	Write(count());
	Write(" ");
	WriteLine("====================");
	
	foreach( var i in range(2,8) )
	{
		Write(i);
		Write(" ");
	}

	WriteLine("====================");
	
	var loop = function() -> num
	{
		foreach( var i in range(3,9) )
		{
			yield return i;
		}
	};
	
	foreach( var i in loop )
	{
		Write(i);
		Write(" ");
	}

	WriteLine("====================");

	var loop2 = function() -> string
	{
		foreach( var i in ["torben", "lars", "eric"] )
		{
			yield return i;
		}
	};
	
	foreach( var s in loop2 )
	{
		Write(s);
		Write(" ");
	}

	WriteLine("====================");

	public class Source
	{
		public property num Zahl { get; set; }
		public property string Text { get; set; }
	}
	
	public class Sink
	{
		public property num Ausgabe { get; set; }
		public property string TextAusgabe { get; set; }
	}

	
	var source = new Source();
	var sink = new Sink();
	var sink2 = new Sink();
	var sink3 = new Sink();
	var sink4 = new Sink();
	var sink5 = new Sink();

	source.Zahl = 123;	
	source.Zahl => sink.Ausgabe;
	Write(sink.Ausgabe);
	WriteLine(" = 123 ====================");

	source.Zahl = 124;	
	Write(sink.Ausgabe);
	WriteLine(" = 124 ====================");
	
	source.Zahl => a -> a*2 => sink2.Ausgabe;
	Write(sink2.Ausgabe);
	WriteLine(" = 248 ====================");

	source.Zahl = 125;	
	Write(sink2.Ausgabe);
	WriteLine(" = 250 ====================");
	
	var bind = source.Zahl => a -> a+4;
	bind => sink3.Ausgabe;
	Write(sink3.Ausgabe);
	WriteLine(" = 129 ====================");

	source.Zahl = 126;	
	Write(sink3.Ausgabe);
	WriteLine(" = 130 ====================");

	var bind2 = function(num a) -> num { return a / 2; } => sink4.Ausgabe;
	var bind3 = source.Zahl => bind2;
	Write(sink4.Ausgabe);
	WriteLine(" = 63 ====================");
	source.Zahl = 127;	
	Write(sink4.Ausgabe);
	WriteLine(" = 63.5 ====================");
	
	bind3 => sink5.Ausgabe;
	Write(sink5.Ausgabe);
	WriteLine(" = 63.5 ====================");
	
	var num=>num bind4 = source.Zahl => a -> a+4;
	var num=>num bind5 = a->a+3 => sink.Ausgabe;
	
	public class XMLOutput
	{
		private var Node node;
		
		public property Node XML
		{
			get
			{
				return this.node;
			}
			set
			{
				this.node = value;
				Write(this.node);
			}
		}
	}
		
	var string=>Node bind6 = a -> <span style="font-weight:bold; color:#FF0000">{a}</span>;
	var string=>div bind7 = bind6 => x -> <div>{x}</div>;
	source.Text => bind7;
	
	source.Text = "Hallo XML Welt";
	var out = new XMLOutput();
	bind7 => out.XML;

	WriteLine(" XML ====================");

	source.Text = "Neue XML Welt";
	
	var mystr = "Hallo";
	Write(mystr.Length);
	WriteLine("====================" + mystr[0]);
	mystr = "Torben,Martin,Mirko,Arno";
	foreach( var str in mystr.Split(",") )
	{
		Write(str + " and ");		
	}
	WriteLine("====================");

	Write(string.FromCharCode(65));
	Write(string.FromCharCode(66));
	Write(string.FromCharCode(67));
	WriteLine("====================");

	// var Car=>string newBind = car -> ($car.MaxSpeed).ToString() + " km/h";
	
	var void=>string newBind2 = (&auto.MaxSpeed).ToString() + " km/h";
	var sink6 = new Sink();
	newBind2 => sink6.TextAusgabe;
	Write(sink6.TextAusgabe);
	WriteLine(" = 231 km/h ====================");
	
	auto.MaxSpeed = 120;
	Write(sink6.TextAusgabe);
	WriteLine(" = 120 km/h ====================");

	var auto2 = new Car();
	auto2.Kennzeichen = "DU-BW 143";
	auto2.MaxSpeed = 80;
	
	public class Garage
	{
		public property Car Auto { get; set; }
		public property num Debug { get; set; }
	}

	var g = new Garage();
	g.Auto = auto;
	g.Debug = 1;
	
	var sink7 = new Sink();
	(&(&g.Auto).MaxSpeed).ToString() + " km/h debug=" + g.Debug.ToString() => sink7.TextAusgabe;
	Write(sink7.TextAusgabe);
	WriteLine(" = 120 km/h ====================");
	
	g.Debug = 2;
	auto.MaxSpeed = 130;
	Write(sink7.TextAusgabe);
	WriteLine(" = 130 km/h ====================");
		
	g.Debug = 3;
	g.Auto = auto2;
	Write(sink7.TextAusgabe);
	WriteLine(" = 80 km/h ====================");
		
	g.Debug = 4;
	auto2.MaxSpeed = 90;
	Write(sink7.TextAusgabe);
	WriteLine(" = 90 km/h ====================");

	g.Debug = 5;
	auto.MaxSpeed = 300;
	Write(sink7.TextAusgabe);
	WriteLine(" = 90 km/h ====================");
		
	// c.Malfunction("Bodo");
	
	var source2 = new Source();
	source2.Text = "#FFFF00";
	source2.Text => xml.style.backgroundColor;
	
	var xml5 = <input type="submit" value="Green" click={a -> (source2.Text = "green")} />;
	Write(xml5);

	var xml6 = <input type="text" value="Empty" />;
	Write(xml6);
	
	var timer = new System.Timer(2000, true);
	timer.Timeout => function(System.Timer t) -> string { var i = 0; while( true ) { i++; yield return i.ToString(); } } => xml6.value;
	timer.Start();
	
	var xml7 = <div style="position:absolute; font-size:24pt; color:red; top:20px">ANIMATION</div>;	
	Write(xml7);
	var anim = new System.LinearAnimation( 20, 600, 4000 );
	anim.PositionString => xml7.style.left;
	anim.Start();
}